/*
	Defines the module used to connect to SQLite-based databases
*/
#include "SQLiteDatabaseDriver.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>

extern int errno;

SQLiteDatabaseDriver::SQLiteDatabaseDriver()
{
	dbPtr = NULL;
}

SQLiteDatabaseDriver::~SQLiteDatabaseDriver()
{
	Close();
}

/*
	Open: open a connection to the database
	Close: close the connection to the database
	Query: make a database query and return the result
	Exec: make a database query and ignore the result
	BatchExec: perform a set of queries located in a file
*/

bool SQLiteDatabaseDriver::Open(DatabaseConnectionInfo* info)
{
	std::string file = info->DB;
	int res = sqlite3_open_v2(file.c_str(), &(this->dbPtr), SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
	if (res)
	{
		fprintf(stderr, "SQLiteDatabaseDriver.Open: Error opening `%s`: %s.\n", file.c_str(), sqlite3_errmsg(dbPtr));
		Close();
		return false;
	}
	return true;
}

void SQLiteDatabaseDriver::Close()
{
	sqlite3_stmt* statement;
	if (dbPtr)
	{
		while ((statement = sqlite3_next_stmt(dbPtr, 0)) != 0)
		{
			sqlite3_finalize(statement);
		}
		sqlite3_close(dbPtr);
		dbPtr = NULL;
	}
}

IDBQueryResult* SQLiteDatabaseDriver::Query(DBQuery& query)
{
	std::string sql = query.GetQuery();
	sqlite3_stmt* statement;
	IDBQueryResult* res;
	if (sqlite3_prepare_v2(dbPtr, sql.c_str(), sql.length(), &statement, NULL) != SQLITE_OK)
	{
		fprintf(stderr, "SQLiteDatabaseDriver.Exec: Error executing statement `%s`. %s\n", sql.c_str(), sqlite3_errmsg(dbPtr));
		return NULL;
	}
	res = new SQLiteDBQueryResult(dbPtr, statement);
	if (res->NextRow())
	{
		return res;
	}
	else
	{
		delete res;
		return NULL;
	}
}

void SQLiteDatabaseDriver::Exec(DBQuery& query)
{
	char* errMsg;
	std::string sql = query.GetQuery();
	if (dbPtr)
	{
		if (sqlite3_exec(dbPtr, sql.c_str(), NULL, NULL, &errMsg) != SQLITE_OK)
		{
			fprintf(stderr, "SQLiteDatabaseDriver.Exec: Error executing statement `%s`. %s\n", sql.c_str(), errMsg);
		}
		sqlite3_free(errMsg);
	}
}

void SQLiteDatabaseDriver::BatchExec(std::string fileName)
{
	std::string str;
	char temp[1024];
	FILE* file = fopen(fileName.c_str(), "r");
	char* errMsg;
	memset(temp, 0, sizeof(temp));
	if (!file)
	{
		fprintf(stderr, "SQLiteDatabaseDriver.BatchExec: Error opening `%s`. Error code: %d.\n", fileName.c_str(), errno);
		return;
	}
	while (!feof(file))
	{
		fread(temp, sizeof(char), 1024, file);
		str.append(temp);
		memset(temp, 0, sizeof(temp));
	}
	fclose(file);
	if (sqlite3_exec(dbPtr, str.c_str(), NULL, NULL, &errMsg) != SQLITE_OK)
	{
		fprintf(stderr, "SQLiteDatabaseDriver.BatchExec: Error executing statement in %s. %s\n", fileName.c_str(), errMsg);
	}
	sqlite3_free(errMsg);
}

SQLiteDBQueryResult::SQLiteDBQueryResult(sqlite3* dbHandle, sqlite3_stmt* statement)
{
	DBHandle = dbHandle;
	Handle = statement;
	Names.clear();
	std::string name;
	int i, j = sqlite3_column_count(Handle);
	for (i = 0; i < j; i++)
	{
		name = sqlite3_column_name(Handle, i);
		std::transform(name.begin(), name.end(), name.begin(), tolower);
		Names[name] = i;
	}
}

SQLiteDBQueryResult::~SQLiteDBQueryResult()
{
	sqlite3_finalize(Handle);
}

long SQLiteDBQueryResult::GetDoubleWord(std::string n)
{
	std::transform(n.begin(), n.end(), n.begin(), tolower);
	std::map<const std::string, unsigned int>::iterator iter = Names.find(n);
	if (iter != Names.end())
	{
		return sqlite3_column_int(Handle, iter->second);
	}
	return 0;
}

long long SQLiteDBQueryResult::GetQuadWord(std::string n)
{
	std::transform(n.begin(), n.end(), n.begin(), tolower);
	std::map<const std::string, unsigned int>::iterator iter = Names.find(n);
	if (iter != Names.end())
	{
		return sqlite3_column_int64(Handle, iter->second);
	}
	return 0;
}

std::string SQLiteDBQueryResult::GetString(std::string n)
{
	std::transform(n.begin(), n.end(), n.begin(), tolower);
	std::map<const std::string, unsigned int>::iterator iter = Names.find(n);
	std::string ret = "";
	const char* val = NULL;
	if (iter != Names.end())
	{
		val = (const char*)sqlite3_column_text(Handle, iter->second);
		if (val != NULL)
		{
			ret = val;
		}
		else
		{
			ret = sqlite3_errmsg(DBHandle);
		}
	}
	else
	{
		ret = "column not found";
	}
	return ret;
}

const char* SQLiteDBQueryResult::GetBinaryData(std::string n, unsigned long& size)
{
	std::transform(n.begin(), n.end(), n.begin(), tolower);
	std::map<const std::string, unsigned int>::iterator iter = Names.find(n);
	const char* ret = NULL;
	if (iter != Names.end())
	{
		ret = (const char*)sqlite3_column_blob(Handle, iter->second);
		size = sqlite3_column_bytes(Handle, iter->second);
	}
	return ret;
}

bool SQLiteDBQueryResult::NextRow()
{
	return sqlite3_step(Handle) == SQLITE_ROW;
}

/*
	Create and return an instance of SQLiteDatabaseDriver
*/
extern "C" void* InitModule(void* d)
{
	IDatabase* ptr = new SQLiteDatabaseDriver();
	return ptr;
}

/*
	Destroy an instance of SQLiteDatabaseDriver
*/
extern "C" void* DestroyModule(void* d)
{
	IDatabase* ptr = (IDatabase*)(d);
	delete ptr;
	return NULL;
}
